package com.childenglish.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 在线用户管理服务
 * 用于跟踪和管理同时在线登录的用户
 */
@Service
public class OnlineUserService {

    private static final Logger logger = LoggerFactory.getLogger(OnlineUserService.class);

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private static final String ONLINE_USER_PREFIX = "online:user:";
    private static final String USER_SESSIONS_PREFIX = "user:sessions:";
    private static final String USER_SESSION_PREFIX = "user:session:"; // userId:sessionId 组合键
    private static final int SESSION_TIMEOUT = 1800; // 30分钟，与Session超时时间一致

    /**
     * 记录用户登录（单会话模式：同一用户只能有一个会话）
     * @param userId 用户ID
     * @param username 用户名
     * @param role 角色
     * @param sessionId 会话ID
     * @param ipAddress IP地址
     * @param userAgent 用户代理
     */
    public void recordUserLogin(Long userId, String username, String role, String sessionId, 
                                String ipAddress, String userAgent) {
        // 使用 userId:sessionId 组合作为唯一标识，支持同一sessionId被不同用户使用
        String userSessionKey = USER_SESSION_PREFIX + userId + ":" + sessionId;
        
        // 单会话模式：清理该用户的其他会话（只保留当前会话）
        String userSessionsKey = USER_SESSIONS_PREFIX + userId;
        Set<Object> oldSessionIds = redisTemplate.opsForSet().members(userSessionsKey);
        if (oldSessionIds != null && !oldSessionIds.isEmpty()) {
            for (Object oldSessionIdObj : oldSessionIds) {
                String oldSessionId = oldSessionIdObj.toString();
                if (!oldSessionId.equals(sessionId)) {
                    // 删除该用户的其他会话（使用 userId:sessionId 组合键）
                    String oldUserSessionKey = USER_SESSION_PREFIX + userId + ":" + oldSessionId;
                    redisTemplate.delete(oldUserSessionKey);
                    redisTemplate.opsForSet().remove(userSessionsKey, oldSessionId);
                    logger.info("单会话模式：清理用户 {} ({}) 的旧会话: {}", userId, username, oldSessionId);
                }
            }
        }
        
        // 记录用户会话信息
        Map<String, Object> sessionInfo = new HashMap<>();
        sessionInfo.put("userId", userId);
        sessionInfo.put("username", username);
        sessionInfo.put("role", role);
        sessionInfo.put("sessionId", sessionId);
        sessionInfo.put("ipAddress", ipAddress);
        sessionInfo.put("userAgent", userAgent);
        sessionInfo.put("loginTime", System.currentTimeMillis());
        sessionInfo.put("lastAccessTime", System.currentTimeMillis());

        // 存储会话信息（使用 userId:sessionId 组合键），设置过期时间
        redisTemplate.opsForValue().set(userSessionKey, sessionInfo, SESSION_TIMEOUT, TimeUnit.SECONDS);

        // 将sessionId添加到用户的会话集合中
        redisTemplate.opsForSet().add(userSessionsKey, sessionId);
        redisTemplate.expire(userSessionsKey, SESSION_TIMEOUT, TimeUnit.SECONDS);
    }

    /**
     * 更新用户最后访问时间
     * @param userId 用户ID
     * @param sessionId 会话ID
     */
    public void updateLastAccessTime(Long userId, String sessionId) {
        String userSessionKey = USER_SESSION_PREFIX + userId + ":" + sessionId;
        Object sessionObj = redisTemplate.opsForValue().get(userSessionKey);
        if (sessionObj instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> sessionInfo = (Map<String, Object>) sessionObj;
            sessionInfo.put("lastAccessTime", System.currentTimeMillis());
            redisTemplate.opsForValue().set(userSessionKey, sessionInfo, SESSION_TIMEOUT, TimeUnit.SECONDS);
        }
    }

    /**
     * 记录用户登出（清理会话和在线状态）- 指定用户ID版本
     * @param userId 用户ID
     * @param sessionId Spring Session的sessionId
     */
    public void recordUserLogout(Long userId, String sessionId) {
        // 使用 userId:sessionId 组合键，只清理指定用户的会话
        String userSessionKey = USER_SESSION_PREFIX + userId + ":" + sessionId;
        Object sessionObj = redisTemplate.opsForValue().get(userSessionKey);
        
        if (sessionObj instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> sessionInfo = (Map<String, Object>) sessionObj;
            String username = (String) sessionInfo.get("username");
            
            // 从用户的会话集合中移除
            String userSessionsKey = USER_SESSIONS_PREFIX + userId;
            redisTemplate.opsForSet().remove(userSessionsKey, sessionId);
            
            // 检查用户的会话集合是否为空，如果为空则删除该集合
            Long remainingCount = redisTemplate.opsForSet().size(userSessionsKey);
            if (remainingCount == null || remainingCount == 0) {
                redisTemplate.delete(userSessionsKey);
                logger.info("用户 {} ({}) 登出，已清理所有会话和在线状态", userId, username);
            } else {
                logger.info("用户 {} ({}) 登出，剩余会话数: {}", userId, username, remainingCount);
            }
            
            // 删除用户会话信息（使用 userId:sessionId 组合键）
            redisTemplate.delete(userSessionKey);
            logger.info("已清理用户 {} ({}) 的会话: {}", userId, username, sessionId);
        } else {
            logger.warn("未找到用户 {} 的会话信息: sessionId={}", userId, sessionId);
        }
    }
    
    /**
     * 记录用户登出（清理会话和在线状态）- 仅sessionId版本（兼容旧版本）
     * @param sessionId Spring Session的sessionId
     */
    public void recordUserLogout(String sessionId) {
        // 查找所有使用该sessionId的用户会话（因为可能多个用户使用同一个sessionId）
        Set<String> allUserSessionKeys = redisTemplate.keys(USER_SESSION_PREFIX + "*:" + sessionId);
        
        if (allUserSessionKeys != null && !allUserSessionKeys.isEmpty()) {
            for (String userSessionKey : allUserSessionKeys) {
                Object sessionObj = redisTemplate.opsForValue().get(userSessionKey);
                
                if (sessionObj instanceof Map) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> sessionInfo = (Map<String, Object>) sessionObj;
                    Long userId = sessionInfo.get("userId") != null ? 
                        ((Number) sessionInfo.get("userId")).longValue() : null;
                    String username = (String) sessionInfo.get("username");
                    
                    if (userId != null) {
                        // 从用户的会话集合中移除
                        String userSessionsKey = USER_SESSIONS_PREFIX + userId;
                        redisTemplate.opsForSet().remove(userSessionsKey, sessionId);
                        
                        // 检查用户的会话集合是否为空，如果为空则删除该集合
                        Long remainingCount = redisTemplate.opsForSet().size(userSessionsKey);
                        if (remainingCount == null || remainingCount == 0) {
                            redisTemplate.delete(userSessionsKey);
                            logger.info("用户 {} ({}) 登出，已清理所有会话和在线状态", userId, username);
                        } else {
                            logger.info("用户 {} ({}) 登出，剩余会话数: {}", userId, username, remainingCount);
                        }
                    }
                }
                
                // 删除用户会话信息（使用 userId:sessionId 组合键）
                redisTemplate.delete(userSessionKey);
            }
        }
        
        logger.info("已清理会话: {}", sessionId);
    }

    /**
     * 获取所有在线用户
     */
    public List<Map<String, Object>> getAllOnlineUsers() {
        // 第一步：收集所有有效的会话信息（按userId分组），并清理长时间未活动的会话
        Map<Long, List<Map<String, Object>>> sessionsByUserId = new HashMap<>();
        Set<String> allSessionKeys = redisTemplate.keys(USER_SESSION_PREFIX + "*");
        long currentTime = System.currentTimeMillis();
        long inactiveThreshold = 15 * 60 * 1000; // 15分钟未活动视为过期
        
        if (allSessionKeys != null) {
            logger.debug("查询Redis中的会话，共找到 {} 个会话Key", allSessionKeys.size());
            List<String> expiredSessions = new ArrayList<>();
            
            for (String userSessionKey : allSessionKeys) {
                Object sessionObj = redisTemplate.opsForValue().get(userSessionKey);
                if (sessionObj instanceof Map) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> sessionInfo = (Map<String, Object>) sessionObj;
                    
                    String sessionId = (String) sessionInfo.get("sessionId");
                    Long userId = sessionInfo.get("userId") != null ? 
                        ((Number) sessionInfo.get("userId")).longValue() : null;
                    String username = (String) sessionInfo.get("username");
                    
                    // 检查会话是否长时间未活动
                    Long lastAccessTime = ((Number) sessionInfo.getOrDefault("lastAccessTime", 0L)).longValue();
                    long inactiveMinutes = (currentTime - lastAccessTime) / 60000;
                    
                    if (currentTime - lastAccessTime > inactiveThreshold) {
                        // 会话长时间未活动，标记为过期
                        expiredSessions.add(userSessionKey);
                        logger.debug("标记为过期会话: sessionId={}, userId={}, username={}, 最后访问时间: {}, 已过期: {}分钟", 
                            sessionId, userId, username, new java.util.Date(lastAccessTime), inactiveMinutes);
                        continue;
                    }
                    
                    if (userId != null && username != null) {
                        logger.debug("找到有效会话: sessionId={}, userId={}, username={}, 最后访问时间: {}, 未活动: {}分钟", 
                            sessionId, userId, username, new java.util.Date(lastAccessTime), inactiveMinutes);
                        sessionsByUserId.computeIfAbsent(userId, k -> new ArrayList<>()).add(sessionInfo);
                    } else {
                        logger.warn("会话信息不完整: sessionId={}, userId={}, username={}", sessionId, userId, username);
                    }
                } else {
                    logger.warn("会话对象不是Map类型: userSessionKey={}", userSessionKey);
                }
            }
            
            if (!expiredSessions.isEmpty()) {
                logger.debug("共标记 {} 个过期会话需要清理", expiredSessions.size());
            }
            
            // 清理过期的会话
            for (String expiredUserSessionKey : expiredSessions) {
                Object sessionObj = redisTemplate.opsForValue().get(expiredUserSessionKey);
                if (sessionObj instanceof Map) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> sessionInfo = (Map<String, Object>) sessionObj;
                    String sessionId = (String) sessionInfo.get("sessionId");
                    Long userId = sessionInfo.get("userId") != null ? 
                        ((Number) sessionInfo.get("userId")).longValue() : null;
                    
                    // 从Redis删除会话（使用 userId:sessionId 组合键）
                    redisTemplate.delete(expiredUserSessionKey);
                    
                    // 从用户的会话集合中移除
                    if (userId != null && sessionId != null) {
                        String userSessionsKey = USER_SESSIONS_PREFIX + userId;
                        redisTemplate.opsForSet().remove(userSessionsKey, sessionId);
                        Long remainingCount = redisTemplate.opsForSet().size(userSessionsKey);
                        if (remainingCount == null || remainingCount == 0) {
                            redisTemplate.delete(userSessionsKey);
                        }
                    }
                }
            }
        }
        
        // 第二步：验证并清理用户会话集合，确保数据一致性
        Set<String> allUserSessionKeys = redisTemplate.keys(USER_SESSIONS_PREFIX + "*");
        if (allUserSessionKeys != null) {
            for (String userSessionsKey : allUserSessionKeys) {
                String userIdStr = userSessionsKey.substring(USER_SESSIONS_PREFIX.length());
                try {
                    Long userId = Long.parseLong(userIdStr);
                    Set<Object> sessionIds = redisTemplate.opsForSet().members(userSessionsKey);
                    
                    if (sessionIds != null && !sessionIds.isEmpty()) {
                        List<String> invalidSessionIds = new ArrayList<>();
                        
                        for (Object sessionIdObj : sessionIds) {
                            String sessionId = sessionIdObj.toString();
                            // 使用 userId:sessionId 组合键查询
                            String userSessionKey = USER_SESSION_PREFIX + userId + ":" + sessionId;
                            Object sessionObj = redisTemplate.opsForValue().get(userSessionKey);
                            
                            if (sessionObj instanceof Map) {
                                @SuppressWarnings("unchecked")
                                Map<String, Object> sessionInfo = (Map<String, Object>) sessionObj;
                                
                                if (sessionInfo.get("userId") != null) {
                                    Long sessionUserId = ((Number) sessionInfo.get("userId")).longValue();
                                    if (!sessionUserId.equals(userId)) {
                                        // 会话属于其他用户，从当前用户的集合中移除
                                        logger.warn("会话 {} 中的userId({})与key中的userId({})不一致，从集合中移除", 
                                            sessionId, sessionUserId, userId);
                                        invalidSessionIds.add(sessionId);
                                    }
                                } else {
                                    // 会话信息不完整
                                    invalidSessionIds.add(sessionId);
                                }
                            } else {
                                // 会话不存在（已过期）
                                invalidSessionIds.add(sessionId);
                            }
                        }
                        
                        // 清理无效的会话ID
                        if (!invalidSessionIds.isEmpty()) {
                            for (String invalidSessionId : invalidSessionIds) {
                                redisTemplate.opsForSet().remove(userSessionsKey, invalidSessionId);
                            }
                        }
                        
                        // 如果用户的会话集合为空，删除该集合
                        Long remainingCount = redisTemplate.opsForSet().size(userSessionsKey);
                        if (remainingCount == null || remainingCount == 0) {
                            redisTemplate.delete(userSessionsKey);
                        }
                    } else {
                        // 会话集合为空，删除该集合
                        redisTemplate.delete(userSessionsKey);
                    }
                } catch (NumberFormatException e) {
                    // 忽略无效的key
                    logger.warn("无效的用户会话key: {}", userSessionsKey);
                    redisTemplate.delete(userSessionsKey); // 清理无效的key
                }
            }
        }
        
        // 第三步：构建用户列表（基于第一步收集的有效会话）
        Map<Long, Map<String, Object>> userMap = new HashMap<>();
        
        for (Map.Entry<Long, List<Map<String, Object>>> entry : sessionsByUserId.entrySet()) {
            Long userId = entry.getKey();
            List<Map<String, Object>> sessions = entry.getValue();
            
            if (!sessions.isEmpty()) {
                // 找到最新的会话信息
                Map<String, Object> latestSessionInfo = null;
                long latestAccessTime = 0;
                
                for (Map<String, Object> sessionInfo : sessions) {
                    Long accessTime = ((Number) sessionInfo.getOrDefault("lastAccessTime", 0L)).longValue();
                    if (accessTime > latestAccessTime) {
                        latestAccessTime = accessTime;
                        latestSessionInfo = sessionInfo;
                    }
                }
                
                if (latestSessionInfo != null) {
                    // 获取该用户的会话数量（从Redis集合中获取，确保准确性）
                    String userSessionsKey = USER_SESSIONS_PREFIX + userId;
                    Long sessionCount = redisTemplate.opsForSet().size(userSessionsKey);
                    int validSessionCount = (sessionCount != null && sessionCount > 0) ? sessionCount.intValue() : sessions.size();
                    
                    Map<String, Object> userInfo = new HashMap<>();
                    userInfo.put("userId", userId);
                    userInfo.put("username", latestSessionInfo.get("username"));
                    userInfo.put("role", latestSessionInfo.get("role"));
                    userInfo.put("ipAddress", latestSessionInfo.get("ipAddress"));
                    userInfo.put("loginTime", latestSessionInfo.get("loginTime"));
                    userInfo.put("lastAccessTime", latestSessionInfo.get("lastAccessTime"));
                    userInfo.put("sessionCount", validSessionCount);
                    
                    userMap.put(userId, userInfo);
                }
            }
        }
        
        // 简化日志输出，只保留关键信息
        logger.debug("在线用户统计: 共 {} 个用户", userMap.size());
        if (logger.isDebugEnabled() && !userMap.isEmpty()) {
            for (Map.Entry<Long, Map<String, Object>> entry : userMap.entrySet()) {
                Map<String, Object> userInfo = entry.getValue();
                logger.debug("在线用户 - ID: {}, 用户名: {}, 角色: {}, 会话数: {}", 
                    entry.getKey(), 
                    userInfo.get("username"), 
                    userInfo.get("role"),
                    userInfo.get("sessionCount"));
            }
        }
        
        return new ArrayList<>(userMap.values());
    }

    /**
     * 获取指定用户的在线会话数
     */
    public int getUserSessionCount(Long userId) {
        String userSessionsKey = USER_SESSIONS_PREFIX + userId;
        Long count = redisTemplate.opsForSet().size(userSessionsKey);
        return count != null ? count.intValue() : 0;
    }

    /**
     * 获取在线用户总数（去重后）
     */
    public int getOnlineUserCount() {
        return getAllOnlineUsers().size();
    }

    /**
     * 获取指定用户的所有会话信息
     */
    public List<Map<String, Object>> getUserSessions(Long userId) {
        String userSessionsKey = USER_SESSIONS_PREFIX + userId;
        Set<Object> sessionIds = redisTemplate.opsForSet().members(userSessionsKey);
        List<Map<String, Object>> sessions = new ArrayList<>();
        
        if (sessionIds != null) {
            for (Object sessionIdObj : sessionIds) {
                String sessionId = sessionIdObj.toString();
                // 使用 userId:sessionId 组合键查询
                String userSessionKey = USER_SESSION_PREFIX + userId + ":" + sessionId;
                Object sessionObj = redisTemplate.opsForValue().get(userSessionKey);
                
                if (sessionObj instanceof Map) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> sessionInfo = (Map<String, Object>) sessionObj;
                    sessions.add(sessionInfo);
                }
            }
        }
        
        return sessions;
    }
    
    /**
     * 根据sessionId查找最新的会话信息（用于登出时确定用户）
     * @param sessionId Spring Session的sessionId
     * @return 最新的会话信息，如果找不到返回null
     */
    public Map<String, Object> getLatestSessionBySessionId(String sessionId) {
        // 查找所有使用该sessionId的用户会话
        Set<String> allUserSessionKeys = redisTemplate.keys(USER_SESSION_PREFIX + "*:" + sessionId);
        
        if (allUserSessionKeys != null && !allUserSessionKeys.isEmpty()) {
            Map<String, Object> latestSession = null;
            long latestAccessTime = 0;
            
            for (String userSessionKey : allUserSessionKeys) {
                Object sessionObj = redisTemplate.opsForValue().get(userSessionKey);
                
                if (sessionObj instanceof Map) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> sessionInfo = (Map<String, Object>) sessionObj;
                    
                    // 找到最后访问时间最新的会话
                    Long accessTime = sessionInfo.get("lastAccessTime") != null ? 
                        ((Number) sessionInfo.get("lastAccessTime")).longValue() : 0L;
                    
                    if (accessTime > latestAccessTime) {
                        latestAccessTime = accessTime;
                        latestSession = sessionInfo;
                    }
                }
            }
            
            return latestSession;
        }
        
        return null;
    }
}

